package PopCount(
        popCountNaive, popCountTable, popCountTree, popCountWallace,
        LogK,
        popCountTableWallace, popCountTableTree
        ) where

import List
import Vector
import Wallace
import Tabulate

--@ \subsubsection{PopCount}
--@
--@ \index{PopCount@\te{PopCount} (package)|textbf}

--@ The function \te{popCountNaive} just generates the sum of all the bits
--@ in the input word by extracting them and adding them linearly.
--@ \begin{libverbatim}
--@ function Bit#(m) popCountNaive()
--@   provisos (Add#(a, 1, m));
--@ \end{libverbatim}
popCountNaive :: (Add a 1 m) => Bit n -> Bit m
popCountNaive = foldr (+) 0 ∘ map zExt1 ∘ unpack

zExt1 :: (Add m1 1 m) => Bit 1 -> Bit m
zExt1 = zeroExtend

--@ The function \te{popCountTable} uses a lookup table with the
--@ input as an index to get the population count.
--@ \begin{libverbatim}
--@ function Bit#(m) popCountTable()
--@   provisos (Add#(a, 1, m));
--@ \end{libverbatim}
popCountTable :: (Add a 1 m) => Bit n -> Bit m
popCountTable = tabulate popCountNaive

--@ The function \te{popCountTree} uses a balanced tree
--@ of adders to add the input bits.
--@ \begin{libverbatim}
--@ function Bit#(m) popCountTree()
--@   provisos (Add#(1, b, n), Add#(a, 1, m));
--@ \end{libverbatim}
popCountTree :: (Add 1 b n, Add a 1 m) => Bit n -> Bit m
popCountTree = fold (+) ∘ map zExt1 ∘ unpack

--@ The function \te{popCountWallace} uses a Wallace tree
--@ to add the input bits.
--@ \begin{libverbatim}
--@ function Bit#(m) popCountWallace()
--@   provisos (Add#(1, a, m));
--@ \end{libverbatim}
popCountWallace :: (Add 1 a m) => Bit n -> Bit m
popCountWallace = wallaceAddBags ∘ start ∘ toList ∘ unpack

start :: (Add 1 m n) => List (Bit 1) -> Vector n (List (Bit 1))
start bs = bs :> map (const Nil) genList

-----
-- K=7, LogK=3 also gives good results
-- it gives a better usage of result range, but with a deeper adder.
type K = 8
type LogK = 4

--@ Use a table of a small width, add the results with a Wallace adder.
--@ \begin{libverbatim}
--@ function Bit#(m) popCountTableWallace()
--@   provisos (Add#(LogK, k, m));
--@ \end{libverbatim}
popCountTableWallace :: (Add LogK k m) =>  Bit n -> Bit m
popCountTableWallace = wallaceAdd ∘ List.map popCK ∘ splitInKs 0

--@ Use a table of a small width, add the results with a balanced tree.
--@ \begin{libverbatim}
--@ function Bit#(m) popCountTableTree()
--@   provisos (Add#(a, LogK, m));
--@ \end{libverbatim}
popCountTableTree :: (Add a LogK m) => Bit n -> Bit m
popCountTableTree = List.fold (+) ∘ List.map (zeroExtend ∘ popCK) ∘ splitInKs 0

{-# noinline popCK #-}
popCK :: Bit K -> Bit LogK
popCK = popCountTable

splitInKs :: Nat -> Bit n -> List (Bit k)
splitInKs i bs =
    let i' = fromInteger (valueOf n) `min` (i + fromInteger (valueOf k))
    in  if i == i' then
            Nil
        else
            Cons bs[i'-1:i] (splitInKs i' bs)

--------------------------------------------------------------------------------

{-
interface X =
    ii :: Bit 32 -> Action
    oo :: Bit 6

testPopCount :: Module X
testPopCount =
    module
        i :: Reg (Bit 32) <- mkRegU
        o :: Reg (Bit 6) <- mkRegU
    interface
        ii x = i := x
        oo = o
    rules
        when True
         ==> o := popCountTableWallace i
-}
